1 research outputs found

    Types to the rescue: verification of REST APIs Consumer Code

    Get PDF
    Tese de mestrado, Engenharia Informática (Engenharia de Software) Universidade de Lisboa, Faculdade de Ciências, 2019As arquiteturas de software são fundamentais para o desenvolvimento de um software fiável, escalável e com uma fácil manutenção. Com a criação e crescimento da internet, surgiu a necessidade de criar padrões de software que permitam trocar informação neste novo ambiente. O protocolo SOAP e a arquitetura REST são, dos padrões que emergiram, os que mais se destacaram ao nível da utilização. Durante as últimas décadas, e devido ao grande crescimento daWorld WideWeb, a arquitetura REST tem se destacado como a mais importante e utilizada pela comunidade. REST (Representational State Transfer) retira partido das características do protocolo HTTP para descrever as mensagens trocadas entre clientes e servidores. Os dados na arquitectura REST são representados por recursos, que são identificados por um identificador único (p.e. URI) e que podem ter várias representações (em vários formatos), que são os dados concretos de um recurso. A interação com os recursos é feita usando os métodos HTTP: get para obter um recurso, post para adicionar um novo recurso, put para fazer uma atualização de um recurso, delete para remover um recurso; entre outros, sendo estes os principais para aplicações CRUD. As aplicações RESTful, isto é, aplicações que fornecem os seus serviços através da arquitetura REST, devem ser claras na especificação dos seus serviços de forma a que os seus clientes possam utilizá-las sem erros. Para tal, existem várias linguagens de especificação de APIs REST, como a Open API Specification ou a API Blueprint, no qual é possível descrever formalmente as várias operações fornecidas pelo serviço, como o formato dos pedidos de cada operação e as respetivas respostas. No entanto, estas linguagens apresentam uma limitação nas condições formais que se pode colocar nos parâmetros dos pedidos e no impacto que estes têm no formato e conteúdo da resposta. Deste modo, foi introduzida uma nova linguagem de especificação de aplicações REST, HeadREST, onde é adicionada a expressividade necessária para cobrir as lacunas das outras linguagens. Esta expressividade é introduzida com a utilização de tipos refinados, que permitem restringir os valores de um determinado tipo. Adicionalmente, é introduzida também uma operação que permite verificar se uma determinada expressão pertence a um determinado tipo. Em HeadREST, cada operação é especificada usando uma ou mais asserções. Cada asserção é composta por um método HTTP, um URI template da operação, uma pré-condição que define as condições onde esta operação é aceite, e uma pós-condição que estabelece os resultados da operação se a pré-condição for comprida. Deste modo, estas condições permitem expressar os dados enviados nos pedidos e a receber na resposta, assim como expressar o estado do conjunto de recursos antes e depois do pedido REST. Devido à utilização de tipos refinados não é possível resolver sintaticamente a relação de subtipos na validação de uma especificação HeadREST. Deste modo, é necessária uma abordagem semântica: a relação de subtipos é transformada em fórmulas de lógica de primeira ordem, e depois é utilizado um SMT solver para resolver a formula e, consecutivamente, resolver a relação de subtipos. Por outro lado, é também importante garantir que as chamadas às APIs REST cumprem as especificações das mesmas. As linguagens de programação comuns não conseguem garantir que as chamadas a um serviço REST estão de acordo com a especificação do serviço, nomeadamente se o URL da chamada é válido e se o pedido e resposta estão bem formados ao nível dos valores enviados. Assim, um cliente só percebe se as chamadas estão bem feitas em tempo de execução. Existem poucas soluções para análise estática deste tipo de chamadas (RESType é um raro exemplo) e tendem a ser limitadas e a depender de um único tipo de linguagem de especificação. Para além disso, os clientes de serviços REST tendem a ser maioritariamente desenvolvidos em JavaScript, que possui uma fraca análise estática, o que potencializa ainda mais o problema identificado.Numa primeiro passo para tentar resolver este problema desenvolveu-se a linguagem SafeScript, que se caracteriza por ser um subconjunto do JavaScript equipado com um forte sistema de tipos. O sistema de tipos é muito expressivo graças à adição de tipos refinados e também de um operador que verifica se uma expressão pertence a um tipo. SafeScript apresenta flow typing, isto é, o tipo de uma expressão depende da sua localização no fluxo de controlo do programa. Tal como no HeadREST, não é possível realizar uma simples análise sintática para a validação de tipos. No entanto, neste caso trata-se de uma linguagem imperativa com flow typing, logo uma abordagem igual de tradução direta para um SMT solver não é trivial. Deste modo, a validação de tipos é feita traduzido o código SafeScript para a linguagem intermédia Boogie, onde as necessárias validações são traduzidas como asserções, sendo que o Boogie utiliza internamente o Z3 SMT solver para resolver semanticamente as asserções. Devido à validação semântica, o compilador de SafeScript consegue detetar estaticamente diversos erros de execução comuns, como divisão por zero ou acesso a um array fora dos seus limites, e que não conseguem ser detetados por linguagens similares, como o TypeScript. SafeScript compila para JavaScript, com o intuito de poder ser utilizado em conjunto com este. Graças ao seu expressivo sistema de tipos, o validador de programas SafeScript é também um verificador estático. A partir deste é possível provar que um programa cumpre uma determinada especificação, que pode ser descrita usando os tipos refinados. Neste trabalho destacou-se a capacidade de prova do validador de SafeScript, concretamente resolvendo alguns desafios propostos pelo Verification Benchmarks Challange. A partir do SafeScript desenvolveu-se a extensão SafeRESTScript, que adiciona pedidos REST à sintaxe do SafeScript e valida-os estaticamente de encontro a uma especificação HeadREST. Para cada chamada REST são feitas principalmente duas validações. Em primeiro lugar, é verificado se o URL é um endereço válido do serviço para o método HTTP do pedido, isto é, se existe algum triplo na especificação com o par método e URL do pedido. De seguida, e com a tradução da especificação HeadREST importada para Boogie, é verificado se as chamadas REST cumprem os triplos da especificação, nomeadamente, se as pré-condições são cumpridas então as pós-condições também se devem verificar. Por exemplo, se uma pós-condição, cuja respetiva pré-condição é verdadeira para uma determinada chamada, asserta que no corpo da resposta existe um objeto com o campo id, então um acesso a este campo no corpo da resposta é validado. Neste trabalho, como exemplo ilustrativo das capacidades da linguagem, desenvolveu-se um cliente SafeRESTScript da API REST do conhecido repositório GitHub. Ambas as linguagens possuem um compilador e editor que estão disponíveis como plug-in para o IDE Eclipse, para além de uma versão terminal. As duas linguagens possuem várias limitações, e por isso muito trabalho ainda existe pela frente. No entanto, SafeScript e SafeRESTScript não têm ambição de ser linguagens de produção, mas sim contribuir para um melhoramento da análise estática de programas e mostrar que é possível auxiliar o desenvolvimento fiável de código cliente de serviços REST.REST is the architectural sytle most used in the web to exchange data. RESTful applications must be well documented so clients can use its services without doubts and errors. There are several specification languages for describing REST APIs, e.g. Open API Specification, but they lack on expressiveness to describe the exchanged data. Head- REST specification language was introduced to address this gap, containing an expressive type system that allows to describe rigorously the request and response formats of a service endpoint. On the other hand, it is also important to ensure that REST calls in client code meet the service specification. This challenge is even more important taking in account that most REST clients are made in JavaScript, a weakly typed language. To aim this problem, we firstly developed SafeScript, a subset of JavaScript equipped with a strong type system. SafeScript has a expressive type system thanks to refinement types and to an operator that checks if an expression belongs to a type. A semantic subtyping analysis is necessary; the typing validation in done by translating the code to Boogie intermediate language which uses the Z3 SMT solver for the semantic evaluation. SafeScript compiles directly to JavaScript. SafeRESTScript is an extension of SafeScript that adds REST calls, being a client-side language for consuming REST services. It uses HeadREST specifications to verify REST calls: whether the URL of the call is a valid endpoint and whether the data exchanged match the pre and post-conditions declared in the specification. With the creation of this new languages, we dot not intend in having them as production languages, but to show that it is possible to contribute with a better verification and correction in area where software reliability is weak
    corecore